/*-----------------------------------------------------------------------------
 * Copyright (c) <2013-2015>, <Huawei Technologies Co., Ltd>
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 1. Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation and/
 * or other materials provided with the distribution.
 * 3. Neither the name of the copyright holder nor the names of its contributors
 * may be used to endorse or promote products derived from this software without
 * specific prior written permission.
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 ------------------------------------------------------------------------------
 * Notice of Export Control Law
 ==============================================
 * Huawei LiteOS may be subject to applicable export control laws and regulations,
 * which might include those applicable to Huawei LiteOS of U.S. and the country in
 * which you are located.
 * Import, export and usage of Huawei LiteOS in any manner by you shall be in
 * compliance with such applicable export control laws and regulations.
 *---------------------------------------------------------------------------*/

#include "pthread.h"
#include "dirop_fat.h"
#include "fs/fs.h"
#include "inode/inode.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "errno.h"

#ifdef __cplusplus
#if __cplusplus
extern "C"{
#endif
#endif /* __cplusplus */
#ifdef LOSCFG_FS_FAT
#define OTHER_SIZE (sizeof(dirstream_fat) + 4/*int d_count*/ + 4/*int d_usecount*/)

#define VFAT_IOCTL_READDIR_ALL      _IOR('r', 0x13, struct fat_direntall_buf)
extern const struct mountpt_operations fat_operations;


int vfat_check_path(const char *path)
{
    struct inode *inode_ptr = (struct inode *)NULL;
    char *fullpath = (char *)NULL;

    int ret = vfs_normalize_path((const char *)NULL, path, &fullpath);

    if (ret < 0)
    {
        ret = -ret;
        set_errno(ret);
        return -1;
    }

    inode_ptr = inode_find(fullpath, (const char **)NULL);
    free(fullpath);

    if (inode_ptr && (inode_ptr->u.i_mops == &fat_operations))
    {
        inode_release(inode_ptr);
        return 0;
    }
    else
    {
        return -1;
    }
}


DIR_FAT *opendir_fat(const char *name)
{
    int ret;
    DIR *dp = (DIR *) NULL;
    DIR_FAT *dir_fat = (DIR_FAT *) NULL;

    ret = vfat_check_path(name);
    if (ret)
        return (DIR_FAT *)NULL;

    dp = opendir (name);

    if(dp != NULL)
    {
        dir_fat = (DIR_FAT *)malloc(sizeof(DIR_FAT) + PATH_MAX);
        if(dir_fat != NULL)
        {
            (void)memset_s(dir_fat, sizeof(DIR_FAT) + PATH_MAX, 0, sizeof(DIR_FAT) + PATH_MAX); /*lint !e669*/

            dir_fat->stDirStream.dd_nextloc = 0;
            dir_fat->stDirStream.dd_size = 0;
            dir_fat->stBuf.d_count = 512;
            dir_fat->stBuf.d_usecount = 0;

            (void)pthread_mutex_init(&(dir_fat->stDirStream.dd_lock),(const pthread_mutexattr_t *)NULL);
        }
        else
        {
            goto err;
        }
        dir_fat->stDirStream.dp = dp;
        return dir_fat;
    }

err:
    if(dp != NULL)
    {
        (void)closedir (dp);
    }
    return dir_fat;
}



int closedir_fat(DIR_FAT * dir_fat)
{
    int ret = 0;

    if (dir_fat == NULL) {
        return -1;
    }

    ret = closedir (dir_fat->stDirStream.dp);
    if(0 == ret)
    {
        (void)pthread_mutex_destroy(&(dir_fat->stDirStream.dd_lock));
        free(dir_fat);
    }

    return ret;
}

extern int fatfs_readdir_all(DIR_FAT * dir_fat);

struct fat_direntall *readdir_fat(DIR_FAT * dir_fat)
{
    int ret = 0;
    struct fat_direntall *de = (struct fat_direntall *) NULL;

    if (dir_fat == NULL)
    {
        return (struct fat_direntall *) NULL;
    }

    if(pthread_mutex_lock(&(dir_fat->stDirStream.dd_lock)) != ENOERR)
        return (struct fat_direntall *)NULL; /*lint !e454*/

    ret = fatfs_readdir_all(dir_fat);

    if(ret)
        de = (struct fat_direntall *) NULL;
    else
        de = &(dir_fat->stBuf.direntall);

    if (pthread_mutex_unlock(&(dir_fat->stDirStream.dd_lock)) != ENOERR)
        PRINT_ERR("readdir_fat mutex unlock error \n");
    return de;
}


#define _D_ALLOC_NAMLEN(d) (((char *) (d) + (d)->d_reclen) - &(d)->d_name[0])


int scandir_fat(const char *dir, struct fat_direntall ***namelist,
    int (*selector) (const struct fat_direntall *),
    int (*compar) (const struct fat_direntall **, const struct fat_direntall **))
{
    DIR_FAT *dp = opendir_fat(dir);
    struct fat_direntall *current = (struct fat_direntall *)NULL;
    struct fat_direntall **names = (struct fat_direntall **) NULL;
    unsigned int names_size = 0, pos;
    bool bFailed = 0;

    if (dp == NULL)
        return -1;

    pos = 0;
    while ((current = readdir_fat(dp)) != NULL)
    {
        int use_it = (selector == NULL);

        if (! use_it)
        {
            use_it = (*selector) (current);
        }
        if (use_it)
        {
            struct fat_direntall *vnew = (struct fat_direntall *)NULL;
            unsigned int dsize;

            if (pos == names_size)
            {
                struct fat_direntall **new_direntall = (struct fat_direntall **)NULL;
                if (names_size == 0)
                    names_size = 10;
                else
                    names_size *= 2;
                new_direntall = (struct fat_direntall **)malloc(names_size * sizeof(struct fat_direntall *));
                if (new_direntall == NULL)
                {
                    bFailed = 1;
                    break;
                }

                if (names != NULL)
                {
                    (void)memcpy_s(new_direntall, pos * sizeof(struct fat_direntall *), names, pos * sizeof(struct fat_direntall *));
                    free(names);
                }
                names = new_direntall;
            }

            dsize = current->d_reclen;
            vnew = (struct fat_direntall *) malloc (dsize);

            if (vnew == NULL)
            {
                bFailed = 1;
                break;
            }

            (void)memcpy_s (vnew, dsize, current, dsize);
            names[pos++] = vnew; /*lint !e613*/
        } /*lint !e429*/
    }

    if (1 == bFailed)
    {
        (void)closedir_fat(dp);
        while (pos > 0)
        {
            free (names[--pos]); /*lint !e449 !e613*/
        }

        if (names != NULL)
            free (names);

        return -1;
    }

    (void)closedir_fat(dp);

    /* Sort the list if we have a comparison function to sort with.  */
    if (compar != NULL && names != NULL)
        qsort((void *)names, pos, sizeof (struct fat_direntall *), (int (*)(const void *, const void *))*compar);
    *namelist = names;
    return pos;
}
#endif /* CONFIG_FS_FAT */
#ifdef __cplusplus
#if __cplusplus
}
#endif
#endif /* __cplusplus */

